#pragma once

#include "ExchangeManager.hpp"
#include "MsgQueueManager.hpp"
#include "BindingManager.hpp"
#include "MessageManager.hpp"
#include <google/protobuf/map.h>
#include <string>
#include <memory>

namespace micromq
{
    class VirtualHost
    {
    public:
        using ptr = std::shared_ptr<VirtualHost>;
    public:
        VirtualHost(const std::string& hostName, const std::string& baseDir, const std::string& dbFileName)
            : _hostName(hostName)
            , _exchangeManager(std::make_shared<ExchangeManager>(dbFileName))
            , _bindingManager(std::make_shared<BindingManager>(dbFileName))
            , _msgQueueManager(std::make_shared<MsgQueueManager>(dbFileName))
            , _messageManager(std::make_shared<MessageManager>(baseDir))
        {
            // 恢复历史数据
            std::unordered_map<std::string, MsgQueue::ptr> msgQueues = _msgQueueManager->getAllMsgQueue();
            for(auto& q : msgQueues)
            {
                _messageManager->addMessageCenter(q.first);
            }
        }

        bool declareExchange(const std::string& name, ExchangeType type, bool durable, bool autoDelete, 
            const google::protobuf::Map<std::string, std::string>& args)
        {
            return _exchangeManager->declareExchange(name, type, durable, autoDelete, args);
        }

        void removeExchange(const std::string& name)
        {
            _bindingManager->unbindByEcxchange(name);
            _exchangeManager->removeExchange(name);
        }

        Exchange::ptr getExchangeByName(const std::string& name)
        {
            return _exchangeManager->getExchange(name);
        }

        std::unordered_map<std::string, Exchange::ptr> getAllExchange()
        {
            return _exchangeManager->getAllExchange();
        }

        bool declareMsgQueue(const std::string& name, bool durable, bool exclusive, bool autoDelete, 
            const google::protobuf::Map<std::string, std::string>& args)
        {
            _messageManager->addMessageCenter(name);
            return _msgQueueManager->declareMsgQueue(name, durable, exclusive, autoDelete, args);
        }

        void removeMsgQueue(const std::string& name)
        {
            _messageManager->removeMessageCenter(name);
            _bindingManager->unbindByMsgQueue(name);
            _msgQueueManager->removeMsgQueue(name);
        }

        MsgQueue::ptr getMsgQueueByName(const std::string& msgQueueName)
        {
            return _msgQueueManager->getMsgQueue(msgQueueName);
        }

        std::unordered_map<std::string, MsgQueue::ptr> getAllMsgQueue()
        {
            return _msgQueueManager->getAllMsgQueue();
        }

        bool bind(const std::string& exchangeName, const std::string& msgQueueName, const std::string& bindingKey)
        {
            Exchange::ptr exchange = _exchangeManager->getExchange(exchangeName);
            if(exchange.get() == nullptr)
            {
                logInfo("exchange: %s is not exists, bind faild...", exchangeName.c_str());
                return false;
            }
            MsgQueue::ptr msgQueue = _msgQueueManager->getMsgQueue(msgQueueName);
            if(msgQueue.get() == nullptr)
            {
                logInfo("message queue: %s is not exists, bind faild...", msgQueueName.c_str());
                return false;
            }
            bool durable = exchange->_durable && msgQueue->_durable;
            return _bindingManager->bind(exchangeName, msgQueueName, bindingKey, durable);
        }

        void unbind(const std::string& exchangeName, const std::string& msgQueueName)
        {
            _bindingManager->unbindByEntry(exchangeName, msgQueueName);
        }

        BindingInfo getBindingInfoByEcxchange(const std::string& exchangeName)
        {
            return _bindingManager->getBindingInfoByEcxchange(exchangeName);
        }

        BindingMap getAllBinding()
        {
            return _bindingManager->getAllBinding();
        }

        void produceMessage(const std::string& msgQueueName, BasicProperties* bp, const std::string& content)
        {
            MsgQueue::ptr msgQueue = _msgQueueManager->getMsgQueue(msgQueueName);
            if(msgQueue.get() == nullptr)
            {
                logInfo("message queue: %s is not exists, produce faild...", msgQueueName.c_str());
            }
            _messageManager->produceMessage(msgQueueName, bp, content, msgQueue->_durable);
        }

        MessagePtr consumeMessage(const std::string& msgQueueName)
        {
            return _messageManager->consumeMessage(msgQueueName);
        }

        void ackMessage(const std::string& msgQueueName, const std::string& msgId)
        {
            _messageManager->ackMessage(msgQueueName, msgId);
        }

        std::unordered_map<std::string, MessageCenter::ptr> getMessageCenters()
        {
            return _messageManager->getMessageCenters();
        }

        bool isExchangeExists(const std::string &name)
        {
            return _exchangeManager->isExists(name);
        }

        bool isMsgQueueExists(const std::string &name)
        {
            return _msgQueueManager->isExists(name);
        }

        bool isBidingExists(const std::string &exchangeName, const std::string &msgQueueName)
        {
            return _bindingManager->isExists(exchangeName, msgQueueName);
        }

        void clear()
        {
            _exchangeManager->clear();
            _msgQueueManager->clear();
            _bindingManager->clear();
            _messageManager->clear();
        }

        ~VirtualHost()
        {}
    private:
        std::string _hostName;
        ExchangeManager::ptr _exchangeManager;
        MsgQueueManager::ptr _msgQueueManager;
        BindingManager::ptr _bindingManager;
        MessageManager::ptr _messageManager;
    };
}